﻿using System;
using System.Collections.Generic;
using FinanSoft.Entities;
using FinanSoft.Utilities;
using FinanSoft.DataLayer;

namespace FinanSoft.BusinessLayer
{
    public class MoneyManager
    {
        #region Attributes
        private Currency _localCurrency;
        private Currency _systemCurrency;
        private Dictionary<int, Currency> _currencies;
        private bool _isError;
        private String _errorMessage;
        private bool _definiteError;
        #endregion

        #region Properties
        public bool IsError
        {
            get
            {
                bool tempError = _isError;
                if (!_definiteError)
                    _isError = false;
                return tempError;
            }
        }

        public String ErrorMessage
        {
            get
            {
                String tempError = _errorMessage;
                if (!_definiteError)
                {
                    _isError = false;
                    _errorMessage = "";
                }
                return tempError;
            }
        }

        public Currency LocalCurrency
        {
            get
            {
                return _localCurrency;
            }
        }

        public Currency SystemCurrency
        {
            get
            {
                return _systemCurrency;
            }
        }
        #endregion

        #region Singleton

        private MoneyManager()
        {
            _currencies = new Dictionary<int, Currency>();
            _isError = _definiteError = false;
            List<Currency> currencies = MoneyDataManager.GetInstance().GetCurrencyInformation();
            if (currencies != null)
            {
                foreach (Currency currency in currencies)
                {
                    if (currency.Type.Equals(Constants.CurrencyType.added))
                        _currencies.Add(currency.Id, currency);
                    else if (currency.Type.Equals(Constants.CurrencyType.local))
                        if (_localCurrency == null)
                            _localCurrency = currency;
                        else
                        {
                            _isError = true;
                            _errorMessage = Constants.ERROR_MORE_LOCAL_CURRENCIES;
                        }
                    else if (currency.Type.Equals(Constants.CurrencyType.system))
                        if (_systemCurrency == null)
                            _systemCurrency = currency;
                        else
                        {
                            _isError = true;
                            _errorMessage = Constants.ERROR_MORE_SYSTEM_CURRENCIES;
                        }
                }
                if (_localCurrency == null || _systemCurrency == null)
                {
                    _isError = true;
                    _errorMessage = _localCurrency == null ? Constants.ERROR_MISSING_LOCAL_CURRENCY : Constants.ERROR_MISSING_SYSTEM_CURRENCY;
                }
            }
            else
            {
                _isError = true;
                _errorMessage = Constants.ERROR_DB_CONNECTION;
            }
            _definiteError = _isError;
        }

        /// <summary>
        /// Única instancia de esta clase
        /// </summary>
        private static MoneyManager _instance;

        /// <summary>
        /// Método para implementar el patrón de Singleton
        /// </summary>
        /// <returns>Instancia única de la clase</returns>
        public static MoneyManager GetInstance()
        {
            if (_instance == null)
                _instance = new MoneyManager();
            return _instance;
        }

        #endregion

        #region Public Methods
        /// <summary>
        /// Devuelve todas las monedas que utiliza el módulo, sin contar la del sistema y la local
        /// </summary>
        /// <returns>Lista de monedas, sin local ni sistema</returns>
        public List<Currency> GetAddedCurrencies()
        {
            List<Currency> currencyList = new List<Currency>();
            foreach (Currency currency in _currencies.Values)
                currencyList.Add(currency);
            return currencyList;
        }

        /// <summary>
        /// Devuelve la moneda con el Id indicado, sin contar la del sistema ni la local
        /// </summary>
        /// <param name="currencyId">Id de la moneda a buscar</param>
        /// <returns>Moneda con el id dado</returns>
        public Currency GetCurrency(int currencyId)
        {
            if(_currencies.ContainsKey(currencyId))
                return _currencies[currencyId];
            else if(_localCurrency.Id == currencyId)
                return _localCurrency;
            else if(_systemCurrency.Id == currencyId)
                return _systemCurrency;
            else
                return null;
        }

        /// <summary>
        /// Método que se encarga de devovolver el tipo de moneda 
        /// para un nombre en específico
        /// </summary>
        /// <param name="currencyName">Nombre de la moneda</param>
        /// <returns>Objeto moneda</returns>
        public Currency GetCurrency(String currencyName)
        {
            if (currencyName == SystemCurrency.Name)
                return SystemCurrency;
            else
            {
                foreach (Currency currency in this.GetAddedCurrencies())
                {
                    if (currency.Name == currencyName)
                        return currency;
                }
            }
            return null;
        }

        /// <summary>
        /// Agrega una nueva moneda con la información proporcionada.
        /// </summary>
        /// <param name="symbol">Símbolo de la moneda a agregar. De un caracter</param>
        /// <param name="name">Nombre de la moneda a agregar. De 15 caracteres</param>
        /// <param name="abbreviation">Abreviación de la moneda. De 3 caracteres</param>
        /// <returns>Id de la nueva moneda agregada</returns>
        public int AddCurrency(String symbol, String name, String abbreviation)
        {
            Currency newCurrency = new Currency(symbol, abbreviation, Constants.INVALID_ID, 0, Constants.CurrencyType.invalid, name);
            Currency addedCurrency = MoneyDataManager.GetInstance().AddCurrency(newCurrency);
            if (addedCurrency == null)
            {
                _isError = true;
                _errorMessage = Constants.ERROR_DB_CONNECTION;
                return Constants.INVALID_ID;
            }
            _currencies.Add(addedCurrency.Id, addedCurrency);
            return addedCurrency.Id;
        }

        /// <summary>
        /// Edita la información de la moneda que tiene el id dado. Si no desea editar alguno de los campos viejos
        /// que recibe el método, debe enviar la información vieja del campo. No se puede editar la moneda del sistema
        /// o la moneda local.
        /// </summary>
        /// <param name="symbol">Símbolo de la moneda editada</param>
        /// <param name="name">Nombre de la moneda editada</param>
        /// <param name="abbreviation">Abreviación de la moneda editada</param>
        /// <param name="id">Id de la moneda que se quiere cambiar</param>
        /// <returns>Devuelve si se pudo o no editar la moneda</returns>
        public bool EditCurrency(String symbol, String name, String abbreviation, int id)
        {
            Currency newCurrency = new Currency(symbol, abbreviation, id, 0, Constants.CurrencyType.invalid, name);
            bool wasEdited = MoneyDataManager.GetInstance().EditCurrency(newCurrency);
            if (!wasEdited)
            {
                _isError = true;
                _errorMessage = Constants.ERROR_DB_CONNECTION;
                return false;
            }
            _currencies[id].Name = name;
            _currencies[id].Symbol = symbol;
            _currencies[id].Abbreviation = abbreviation;
            return true;
        }

        /// <summary>
        /// Elimina una moneda de la base de datos. Dicha moneda no puede estar asociada a ninguna cuenta ni tener
        /// un tipo de cambio en el historial.
        /// </summary>
        /// <param name="id">Id de la moneda que se quiere eliminar</param>
        /// <returns>Devuelve si se eliminó la moneda exitósamente</returns>
        public bool DeleteCurrency(int id)
        {
            bool wasDeleted = MoneyDataManager.GetInstance().DeleteCurrency(id);
            if (!wasDeleted)
            {
                _isError = true;
                _errorMessage = Constants.ERROR_CURRENCY_DELETE_FAILED;
                return false;
            }
            _currencies.Remove(id);
            return true;
        }

        /// <summary>
        /// Establece el tipo de cambio para una moneda en específico
        /// </summary>
        /// <param name="currencyId">Id de la moneda a la que se le quiere establecer el tipo de cambio.</param>
        /// <param name="initialDate">Fecha a partir de la cuál rige el nuevo tipo de cambio (inclusive)</param>
        /// <param name="endDate">Fecha hasta la cuál rige el nuevo tipo de cambio (inclusive)</param>
        /// <param name="newAmount">Nuevo tipo de cambio de la moneda hacia la moneda local</param>
        /// <returns>Devuelve si se pudo establecer el nuevo tipo de cambio</returns>
        public bool EstablishNewExchangeRate(int currencyId, DateTime initialDate, DateTime endDate, double newAmount)
        {
            //ToDo: Está alambrado el Id del usuario, hace falta hacer un módulo que devuelva el Id del usuario que actualmente está conectado
            bool exchangeRateEstablished = MoneyDataManager.GetInstance().SetCurrencyExchange(currencyId, initialDate, endDate, newAmount, 1);
            if(!exchangeRateEstablished)
            {
                _isError = true;
                _errorMessage = Constants.ERROR_DB_CONNECTION;
                return false;
            }
            if (_systemCurrency.Id == currencyId)
                _systemCurrency.ToLocalCurrency = newAmount;
            else
                _currencies[currencyId].ToLocalCurrency = newAmount;
            return true;
        }

        #endregion
    }
}
